home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 1 (Walnut Creek)
/
Aminet - June 1993 [Walnut Creek].iso
/
usenet
/
sources
/
volume89
/
aplictns
/
graph.1
< prev
next >
Wrap
Internet Message Format
|
1989-11-13
|
29KB
Path: xanth!ukma!tut.cis.ohio-state.edu!gem.mps.ohio-state.edu!wuarchive!texbell!texsun!newstop!sun!swap!page
From: page%swap@Sun.COM (Bob Page)
Newsgroups: comp.sources.amiga
Subject: v89i204: graph - plot mathematical functions, Part01/07
Message-ID: <127782@sun.Eng.Sun.COM>
Date: 13 Nov 89 02:31:49 GMT
Sender: news@sun.Eng.Sun.COM
Lines: 881
Approved: page@sun.com
Submitted-by: dg3i+@andrew.cmu.edu (David Gay)
Posting-number: Volume 89, Issue 204
Archive-name: applications/graph.1
This program draws mathematical functions on a plane.
Text & axes may be added, and the result saved to disk or printed.
To compile it, you'll need:
- Lattice C V5.02
- My eval library (which, if I'm not mistaken, and for obscure reasons,
came over comp.binaries.amiga a few months ago. It's also on fish
disk 192).
- The ARP include files, with a few changes to libraries/arp_pragmas.h:
Add a #ifndef NODOS/#endif around the #pragmas Open to Execute.
- 1 Meg of memory ...
# This is a shell archive.
# Remove anything above and including the cut line.
# Then run the rest of the file through 'sh'.
# Unpacked files will be owned by you and have default permissions.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: SHell ARchive
# Run the following text through 'sh' to create:
# README
# coords.c
# coords.h
# default.c
# default.h
# f_of_x.c
# This is archive 1 of a 7-part kit.
# This archive created: Sun Nov 12 18:23:29 1989
echo "extracting README"
sed 's/^X//' << \SHAR_EOF > README
X This program draws mathematical functions on a plane. Text & axes may
X be added, and the result saved to disk or printed.
X
X To compile it, you'll need:
X
X - Lattice C V5.02
X - My eval library (which, if I'm not mistaken, and for obscure reasons,
X came over comp.binaries.amiga a few months ago. It's also on fish
X disk 192).
X - The ARP include files, with a few changes to libraries/arp_pragmas.h:
X Add a #ifndef NODOS/#endif around the #pragmas Open to Execute.
X - 1 Meg of memory ...
X
X To install the source correctly, create a directory graph, unshar all
X this in it, and Execute mv2dir. On a floppy system, format a new disk
X and unshar onto that ... (You'll have to change a few things because
X the debuggable objects & executable won't fit on one disk).
X
X To compile, the file eval.h (from the eval library) should be accessible
X with a '#include "user/eval.h"'. Now, simply type
X
X lmk -f lmkO ; for an optimised version
X
X lmk ; for a debuggable version
X
X The program is commented, though not extensively. If you want to add any
X objects, read the file object.guidelines first.
X
X
X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
XDavid Gay
X "(p.suiv :=: q.prec.suiv).prec :=: q.prec"
X You don't want to know about this language !
X
XGAY_D@ELMA.EPFL.CH, or GAY_D%ELMA.EPFL.CH@CLSEPF51.bitnet
X(Till mid-august 89)
X
X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
X
SHAR_EOF
echo "extracting coords.c"
sed 's/^X//' << \SHAR_EOF > coords.c
X/*
X * GRAPH, Version 1.00 - 4 August 1989
X *
X * Copyright 1989, David Gay. All Rights Reserved.
X * This software is freely redistrubatable.
X */
X
X/* Set up a coordinate system in a Rastport */
X/* Copyright 1989, David Gay */
X#include <exec/types.h>
X#include <graphics/regions.h>
X#include <intuition/intuition.h>
X#include <proto/graphics.h>
X#include <proto/layers.h>
X
X#include <limits.h>
X#include <math.h>
X
X#include "coords.h"
X#include "tracker.h"
X
Xextern int _FPERR;
X
X/* Actual structure used */
Xstruct RWin {
X struct RWindow rw;
X double xmin, xscale, ymin, yscale;
X double xoffset, yoffset;
X struct Region *clip, *oldRegion;
X int lostpos;
X};
X
X/*-------------------------------------------------------------------------*/
X/* RWindow definition */
X/*-------------------------------------------------------------------------*/
X
X/* convert double to integer, round down. cf floor */
Xlong ftol(double x)
X{
X if (x >= 0) return (long)x;
X else return (long)floor(x);
X}
X
X/* Do Move/Draw style operation func, checking for overflow, etc */
Xstatic void rdo_RWin(struct RWin *this, void (*func)(struct RastPort *rp, long
Xsx, long sy), double x, double y)
X{
X double sx, sy;
X
X if (this->lostpos) func = Move;
X
X _FPERR = 0;
X sx = this->rw.sx((struct RWindow *)this, x);
X sy = this->rw.sy((struct RWIndow *)this, y);
X /* Move, Draw only accept shorts */
X this->lostpos = (_FPERR != 0 || fabs(sx) > SHRT_MAX || fabs(sy) > SHRT_MAX)
X;
X
X if (!this->lostpos) func(this->rw.rp, ftol(sx), ftol(sy));
X}
X
X/* The various conversion routines, to/from ints, for lin orr log scales, x or
Xy ax */
Xstatic double sx_lin(struct RWin *this, double x)
X{
X return (x - this->xmin) * this->xscale + this->xoffset;
X}
X
Xstatic double sx_log(struct RWin *this, double x)
X{
X return (log10(x) - this->xmin) * this->xscale + this->xoffset;
X}
X
Xstatic double sy_lin(struct RWin *this, double y)
X{
X return (y - this->ymin) * this->yscale + this->yoffset;
X}
X
Xstatic double sy_log(struct RWin *this, double y)
X{
X return (log10(y) - this->ymin) * this->yscale + this->yoffset;
X}
X
Xstatic double x_lin(struct RWin *this, long sx)
X{
X return (sx - this->xoffset) / this->xscale + this->xmin;
X}
X
Xstatic double x_log(struct RWin *this, long sx)
X{
X return pow(10.0, (sx - this->xoffset) / this->xscale + this->xmin);
X}
X
Xstatic double y_lin(struct RWin *this, long sy)
X{
X return (sy - this->yoffset) / this->yscale + this->ymin;
X}
X
Xstatic double y_log(struct RWin *this, long sy)
X{
X return pow(10.0, (sy - this->yoffset) / this->yscale + this->ymin);
X}
X
X/* Delete a member of this class */
Xstatic void delete_RWin(struct RWin *this)
X{
X if (this->clip)
X {
X InstallClipRegion(this->rw.rp->Layer, this->oldRegion);
X DisposeRegion(this->clip);
X }
X FreeMem(this, sizeof(struct RWin));
X}
X
X/* Create a coordinate system in Rastport rp (w by h pixels),
X {...}offset : offset in rp at which coords starts (normally > 0)
X {x,y}{min,max} : limits for coords
X logx, logy : logarithmic scale ?
X clip : setup clipping to {...}offset boundaries ?
X*/
Xstruct RWindow *new_RWindow(struct RastPort *rp, long w, long h,
X long leftoffset, long bottomoffset, long rightoffse
Xt, long topoffset,
X double xmin, double ymin, double xmax, double ymax,
X
X long logx, long logy, long clip)
X{
X long width, height;
X struct Rectangle rect;
X struct Region *r;
X struct RWin *this = AllocMem(sizeof(struct RWin), 0L);
X
X if (this)
X {
X /* Setup class methods, and private data */
X this->rw.delete = (void *)delete_RWin;
X this->rw.rdo = (void *)rdo_RWin;
X /* Setup scaling */
X this->rw.rp = rp;
X this->xoffset = leftoffset;
X this->yoffset = h - bottomoffset - 1;
X width = w - leftoffset - rightoffset - 1;
X height = h - bottomoffset - topoffset - 1;
X if (logx)
X {
X this->xmin = log10(xmin);
X this->xscale = width / (log10(xmax) - this->xmin);
X this->rw.sx = (void *)sx_log;
X this->rw.x = (void *)x_log;
X }
X else
X {
X this->xmin = xmin;
X this->xscale = width / (xmax - this->xmin);
X this->rw.sx = (void *)sx_lin;
X this->rw.x = (void *)x_lin;
X }
X if (logy)
X {
X this->ymin = log10(ymin);
X this->yscale = height / (this->ymin - log10(ymax));
X this->rw.sy = (void *)sy_log;
X this->rw.y = (void *)y_log;
X }
X else
X {
X this->ymin = ymin;
X this->yscale = height / (this->ymin - ymax);
X this->rw.sy = (void *)sy_lin;
X this->rw.y = (void *)y_lin;
X }
X
X if (clip)
X {
X /* Setup clipping */
X if (r = NewRegion())
X {
X rect.MinX = leftoffset;
X rect.MaxX = w - rightoffset - 1;
X rect.MinY = topoffset;
X rect.MaxY = h - bottomoffset - 1;
X if (OrRectRegion(r, &rect))
X {
X this->clip = r;
X
X/* Remark: Due to a bug(?) in InstallClipRegion, make sure that the currently
X installed region when EndRefresh is called is not NULL (trashed windows
X otherwise...). */
X this->oldRegion = InstallClipRegion(rp->Layer, r);
X }
X else
X {
X DisposeRegion(r);
X r = NULL;
X }
X }
X if (!r)
X {
X FreeMem(this, sizeof(struct RWin));
X this = NULL;
X }
X }
X else
X this->clip = NULL;
X }
X /* Return the newly allocated instance */
X return (struct RWindow *)this;
X}
X
SHAR_EOF
echo "extracting coords.h"
sed 's/^X//' << \SHAR_EOF > coords.h
X/*
X * GRAPH, Version 1.00 - 4 August 1989
X *
X * Copyright 1989, David Gay. All Rights Reserved.
X * This software is freely redistrubatable.
X */
X
X/* Set up a coordinate system in a Rastport */
X#ifndef COORDS_H
X#define COORDS_H
X
X/* The class used */
Xstruct RWindow {
X struct RastPort *rp; /* The rastport associated woth this coord system */
X /* The methods : */
X void (*delete)(struct RWindow *this);
X /* Do a Movw/Draw, checks for overflow, etc */
X void (*rdo)(struct RWindow *this, void (*func)(struct RastPort *rp, long sx
X, long sy), double x, double y);
X double (*sx)(struct RWindow *this, double x); /* real coords -> rastport co
Xords */
X double (*sy)(struct RWindow *this, double y);
X double (*x)(struct RWindow *this, long sx); /* rastport coords -> real co
Xords */
X double (*y)(struct RWindow *this, long sy);
X};
X
X/* Create a coordinate system in Rastport rp (w by h pixels),
X {x,y}{min,max}offset : offset in rp at which coords starts (normally > 0)
X {x,y}{min,max} : limits for coords
X logx, logy : logarithmic scale ?
X clip : setup clipping to {x,y}{min,max}offset boundaries ?
X*/
Xstruct RWindow *new_RWindow(struct RastPort *rp, long w, long h,
X long xminoffset, long yminoffset, long xmaxoffset,
Xlong ymaxoffset,
X double xmin, double ymin, double xmax, double ymax,
X
X long logx, long logy, long clip);
Xlong ftol(double x); /* convert double to integer, round down. cf floor */
X
Xextern void Move(), BigDraw();
X
X/* Easy calling for Move, Draw in real coords */
X#define RMove(rwin, x, y) ((rwin)->rdo((rwin), Move, (x), (y)))
X#define RDraw(rwin, x, y) ((rwin)->rdo((rwin), BigDraw, (x), (y)))
X
X#endif
X
SHAR_EOF
echo "extracting default.c"
sed 's/^X//' << \SHAR_EOF > default.c
X/*
X * GRAPH, Version 1.00 - 4 August 1989
X *
X * Copyright 1989, David Gay. All Rights Reserved.
X * This software is freely redistrubatable.
X */
X
X/* Default object methods */
X
X#include "object.h"
X#include "object/default.h"
X#include "uio.h"
X
Xint notdone(struct object *this)
X{
X message(this->g, "Routine unimplemented", (char *)NULL);
X
X return FALSE;
X}
X
Xstruct Region *ref_uncalled(struct object *this)
X{
X message(this->g, "Hurrah! You've found a bug!", (char *)NULL);
X
X return NULL;
X}
X
Xint uncalled(struct object *this)
X{
X message(this->g, "Hurrah! You've found a bug!", (char *)NULL);
X
X return FALSE;
X}
X
SHAR_EOF
echo "extracting default.h"
sed 's/^X//' << \SHAR_EOF > default.h
X/*
X * GRAPH, Version 1.00 - 4 August 1989
X *
X * Copyright 1989, David Gay. All Rights Reserved.
X * This software is freely redistrubatable.
X */
X
X/* Default object methods */
X#ifndef DEFAULT_H
X#define DEFAULT_H
X
Xint uncalled(struct object *this);
Xstruct Region *ref_uncalled(struct object *this);
Xint notdone(struct object *this);
X
X#endif
X
SHAR_EOF
echo "extracting f_of_x.c"
sed 's/^X//' << \SHAR_EOF > f_of_x.c
X/*
X * GRAPH, Version 1.00 - 4 August 1989
X *
X * Copyright 1989, David Gay. All Rights Reserved.
X * This software is freely redistrubatable.
X */
X
X#include <exec/types.h>
X#include <intuition/intuition.h>
X#include <graphics/text.h>
X#include <math.h>
X#include <string.h>
X
X#include "object.h"
X#include "object/function.h"
X#include "object/default.h"
X#include "file.h"
X#include "graph.h"
X#include "uio.h"
X#include "coords.h"
X#include "list.h"
X#include "grph.h"
X#include "user/eval.h"
X#include "user/gadgets.h"
X#include "tracker.h"
X
X#include <proto/exec.h>
X#include <proto/intuition.h>
X#include <proto/graphics.h>
X
X/* (private) class f_of_x, inherited from function */
Xstruct f_of_x {
X struct function f;
X char expr[EXPRLEN]; /* the function */
X double oldxmin, oldxmax; /* limits used at last calculation */
X int waslog; /* previous x axis type */
X value function, derivee; /* the compiled function & its differential */
X};
X
X/*-------------------------------------------------------------------------*/
X/* f_of_x class implementation */
X/*-------------------------------------------------------------------------*/
X
X/* Return TRUE if f is displayable */
Xstatic int f_of_x_ok(const struct f_of_x *this)
X{
X return (this->f.min == NOVAL || this->f.max == NOVAL || this->f.min < this-
X>f.max) &&
X (this->f.steps == INOVAL || this->f.steps >= 3);
X}
X
X/* free resources used by this */
Xstatic void destroy_f_of_x(struct f_of_x *this)
X{
X free_var_list(&this->f.used);
X if (this->f.calc) free_list(&this->f.pts, this->f.sizept);
X this->f.calc = FALSE;
X if (this->function) free_expr(this->function);
X if (this->derivee) free_expr(this->derivee);
X this->function = this->derivee = NULL;
X}
X
X/* Initialise dependent parts of f_of_x */
Xstatic int create_f_of_x(struct f_of_x *this)
X{
X this->f.calc = FALSE;
X this->f.var.name = this->f.vname;
X this->function = compile(this->expr);
X if (eval_error != 0)
X {
X message(this->f.o.g, "Compilation error:", eval_messages[eval_error], (
Xchar *)NULL);
X return FALSE;
X }
X this->derivee = differentiate(this->function, this->f.vname);
X if (eval_error != NOT_DIFFERENTIABLE && eval_error != 0)
X {
X message(this->f.o.g, "Differentiation error:", eval_messages[eval_error
X], (char *)NULL);
X return FALSE;
X }
X if (!make_var_list(this->function, &this->f.used))
X init_var_list(&this->f.used);
X return TRUE;
X}
X
X/* Allow the user to edit this function (ref: area to refresh) */
Xstatic int edit_f_of_x(struct f_of_x *this, struct Region **ref)
X{
X struct Requester *req;
X struct Memory *m;
X struct Gadget *gl = NULL, *sd, *nd;
X char from[NBLEN], to[NBLEN], steps[INTLEN], expr[EXPRLEN], xname[VARLEN], c
Xolour[INTLEN];
X int ret = FALSE;
X
X /* Create requester */
X double2str(from, this->f.min);
X double2str(to, this->f.max);
X int2str(steps, this->f.steps);
X int2str(colour, this->f.colour);
X strcpy(expr, this->expr);
X strcpy(xname, this->f.vname);
X
X *ref = NULL;
X if ((m = NewMemory()) &&
X (req = InitReq(50, 20, 255, 145, m)) &&
X SetReqBorder(req, 1, m) &&
X AddIntuiText(&req->ReqText, "Function", 95, 6, m) &&
X AddText(&gl, 0, "f(", FALSE, xname, VARLEN, TRUE, 0, RELVERIFY, 25, 20,
X 25, 10, TRUE, m) &&
X AddText(&gl, 0, ")=", FALSE, expr, EXPRLEN, TRUE, 0, RELVERIFY, 81, 20,
X 160, 10, TRUE, m) &&
X AddText(&gl, 0, "from ", FALSE, from, NBLEN, TRUE, 0, RELVERIFY, 49, 40
X, 80, 10, TRUE, m) &&
X AddText(&gl, 0, "to ", FALSE, to, NBLEN, TRUE, 0, RELVERIFY, 167, 40, 8
X0, 10, TRUE, m) &&
X AddText(&gl, 0, "steps ", FALSE, steps, INTLEN, TRUE, 0, RELVERIFY, 57,
X 60, 32, 10, TRUE, m) &&
X AddText(&gl, 0, "colour ", FALSE, colour, INTLEN, TRUE, 0, RELVERIFY, 1
X56, 60, 32, 10, TRUE, m) &&
X (sd = AddOption(&gl, 0, "Show discontinuities", TRUE, this->f.showdisc
X* SELECTED, 0, 9, 80, 10, 10, m)) &&
X (nd = AddOption(&gl, 0, "Allow flat discontinuities", TRUE, this->f.nic
Xedisc * SELECTED, 0, 9, 100, 10, 10, m)) &&
X AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 40, 120, 65, 15, FALS
XE, m) &&
X AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 145, 120, 65, 15
X, FALSE, m))
X {
X SetReqGadgets(req, gl);
X if (ret = DoRequest(req, this->f.o.g, std_ghandler))
X {
X *ref = full_refresh(this->f.o.g); /* Redraw everything */
X /* Extract typed info */
X this->f.min = str2double(from);
X this->f.max = str2double(to);
X this->f.steps = str2int(steps);
X if ((this->f.colour = str2int(colour)) == INOVAL) this->f.colour =
X1;
X this->f.showdisc = (sd->Flags & SELECTED) != 0;
X this->f.nicedisc = (nd->Flags & SELECTED) != 0;
X strcpy(this->expr, expr);
X strcpy(this->f.vname, xname);
X
X /* Calc new dependent info */
X destroy_f_of_x(this);
X if (this->f.o.ok = f_of_x_ok(this)) this->f.o.ok = create_f_of_x(th
Xis);
X }
X }
X Free(m);
X
X return ret;
X}
X
X/* Calculate the points of the function */
Xstatic int calc_f_of_x(struct f_of_x *this, int allow_mes)
X{
X double x;
X int i;
X struct graph *const g = this->f.o.g;
X /* Use graph limits if none given */
X double const xmin = this->f.min == NOVAL ? g->a.x.min : this->f.min;
X double const xmax = this->f.max == NOVAL ? g->a.x.max : this->f.max;
X int const xlog = g->a.x.log;
X int const steps = (this->f.steps == INOVAL ? DEFSTEPS : this->f.steps) - 1;
X
X double const step = xlog ? pow(xmax / xmin, 1.0 / steps) : (xmax - xmin) /
Xsteps;
X char func[FNAMELEN + 30];
X
X new_list(&this->f.pts);
X
X strcpy(func, "Can't calculate points for ");
X strcat(func, this->f.o.name);
X strcat(func, ":");
X
X if (xmin >= xmax)
X {
X if (allow_mes) message(g, func, "xmin >= xmax", (char *)NULL);
X else alert(g->io.win, "xmin >= xmax", NULL);
X return FALSE;
X }
X if (!create_quick(&this->f.var))
X {
X if (allow_mes) message(g, func, "Couldn't create variable", (char *)NUL
XL);
X else alert(g->io.win, func, "Couldn't create variable");
X return FALSE;
X }
X
X /* For all steps x values (evenly spaced *on screen*) */
X for (i = 0, x = xmin; i <= steps; i++, x = xlog ? x * step : x + step)
X {
X point *pt = alloc_node(this->f.sizept);
X
X if (!pt)
X { /* No mem */
X free_list(&this->f.pts, this->f.sizept);
X free_quick(&this->f.var);
X if (allow_mes) message(g, func, "No memory", (char *)NULL);
X return FALSE;
X }
X add_tail(&this->f.pts, pt);
X
X pt->x = x;
X set_quick(&this->f.var, x);
X pt->y = quick_eval(this->function);
X pt->state = (eval_error == 0) ? EXISTS : 0;
X }
X free_quick(&this->f.var);
X return TRUE;
X}
X
X/* Draw function */
Xstatic void draw_f_of_x(struct f_of_x *this, int allow_mes)
X{
X struct graph *g = this->f.o.g;
X
X /* If xmax or xmin not specified, track values in graph */
X /* ==> function may need recalculating */
X if (this->f.calc)
X if ((this->f.min == NOVAL && this->oldxmin != g->a.x.min) ||
X (this->f.max == NOVAL && this->oldxmax != g->a.x.max) ||
X this->waslog != g->a.x.log)
X {
X this->f.calc = FALSE;
X free_list(&this->f.pts, this->f.sizept);
X }
X
X if (!this->f.calc) this->f.calc = calc_f_of_x(this, allow_mes);
X
X if (this->f.calc)
X {
X this->oldxmin = g->a.x.min;
X this->oldxmax = g->a.x.max;
X this->waslog = g->a.x.log;
X display_function(&this->f);
X }
X}
X
X/* Try to improve look of function by adding points. If fails, decides that
X there is a discontinuity */
Xstatic struct Region *improve_f_of_x(struct f_of_x *this)
X{
X struct graph *const g = this->f.o.g;
X point *pt, *next;
X int ok = FALSE, abort = FALSE, iter;
X /*what y step constitutes a "flat" segment ? Based on window height, could
Xbe better */
X double flat = FLAT * (g->a.y.max - g->a.y.min) / g->io.win->Height;
X char msg[FNAMELEN + 30];
X char pass[20];
X struct Requester *req;
X struct Region *full = NULL;
X
X /* Flat has no meaning when graph incorrect */
X if (!this->f.o.g->ok) flat = 0.0;
X
X if (!this->f.calc)
X {
X strcpy(msg, this->f.o.name);
X strcpy(msg, "not calculated!");
X message(g, msg, (char *)NULL);
X return NULL;
X }
X if (!this->derivee)
X {
X strcpy(msg, this->f.o.name);
X strcat(msg, " wasn't differentiable");
X message(g, msg, (char *)NULL);
X return NULL;
X }
X if (!create_quick(&this->f.var))
X {
X message(g, "Couldn't create variable", (char *)NULL);
X return NULL;
X }
X
X /* Allow user to abort (can take a very long time !) */
X if (!(req = abort_request(g, "Improve: Pass 1")))
X message(g, "No Memory !", (char *)NULL);
X else
X {
X /* Whole graph will need redrawing */
X full = full_refresh(this->f.o.g);
X
X /* Do MAXITER passes, or until every point ok */
X for (iter = 1; iter <= MAXITER && !ok && !abort; iter++)
X {
X sprintf(pass, "Improve: Pass %d", iter);
X set_abort_msg(req, pass);
X ok = TRUE; /* True as long as no improvements made this pass */
X
X /* Scan all but last point */
X for (pt = first(&this->f.pts); succ(next = succ(pt)); pt = next)
X {
X if (aborted(req)) { abort = TRUE; break; }
X
X if ((pt->state & (EXISTS | OK)) == EXISTS) /* Only exists. Igno
Xre points who's segment is "ok" */
X {
X double dx;
X
X pt->state |= OK;
X pt->state &= ~DISC;
X
X /* Idea: check if the differential at this point provides
X a good approximation of the next point. If not, add an
X extra one.
X Bad idea: Use the differential at the mid point of the
X segment. Allows "angles" to remain in the output.
X Remark: I've tried various other schemes. This one was
X the best.
X */
X set_quick(&this->f.var, pt->x);
X dx = quick_eval(this->derivee);
X if (eval_error == 0)
X {
X double ecart = next->y - pt->y;
X /* error: difference between first order taylor approx.
X
X and actual point. */
X double error = fabs(ecart - (next->x - pt->x) * dx);
X
X /* Should we add a point ? error compared with
X difference (on y axis) between the two points, if
X nicedisc, small errors (<2 pixels) are accepted
X without this check */
X if (error > fabs(ecart) * MAXERROR && (!this->f.nicedis
Xc || error > flat))
X {
X /* Add ONE extra point between the two */
X pt->state &= ~OK;
X ok = FALSE; /* We've added a point */
X
X if (iter == MAXITER) pt->state |= DISC; /* This is
X(maybe) a discontinuity */
X else /* currently ignores BREAKUP(Extension: add mo
Xre than one point) */
X {
X point *newpt = alloc_node(this->f.sizept);
X
X if (!newpt)
X {
X message(g, "No memory for point !", (char *
X)NULL);
X abort = TRUE;
X break; /* Exit from loop ! */
X }
X newpt->x = (pt->x + next->x) / 2;
X set_quick(&this->f.var, newpt->x);
X newpt->y = quick_eval(this->function);
X newpt->state = (eval_error == 0) ? EXISTS : 0;
X insert(&this->f.pts, newpt, pt);
X }
X }
X }
X }
X }
X }
X end_abort_request(req);
X }
X free_quick(&this->f.var);
X return full;
X}
X
X/* Provide quick textual form of function */
Xstatic char *f2str_f_of_x(struct f_of_x *this, char *buf, int maxlen)
X{
X buf[maxlen - 1] = '\0';
X strncpy(buf, this->f.o.name, maxlen - 1);
X strncat(buf, "(", maxlen - strlen(buf) - 1);
X strncat(buf, this->f.vname, maxlen - strlen(buf) - 1);
X strncat(buf, ")=", maxlen - strlen(buf) - 1);
X strncat(buf, this->expr, maxlen - strlen(buf) - 1);
X
X return buf;
X}
X
X/* Did user select us ? */
Xstatic int down_f_of_x(struct f_of_x *this)
X{
X struct graph *g = this->f.o.g;
X
X if (this->f.o.ok && this->f.calc) /* visible ? */
X {
X int inside;
X
X if (!create_quick(&this->f.var))
X {
X message(g, "Couldn't create variable", (char *)NULL);
X return FALSE;
X }
X
X set_quick(&this->f.var, g->s.x);
X /* Calculate y = f(x click pos) and compare with y click pos */
X inside = fabs(g->io.rw->sy(g->io.rw, quick_eval(this->function)) - g->i
Xo.rw->sy(g->io.rw, g->s.y)) < FDIST &&
X eval_error == 0;
X free_quick(&this->f.var);
X
X return inside;
X }
X return FALSE;
X}
X
X/* Write f_of_x specific info */
Xstatic int save_f_of_x(struct f_of_x *this, FILE *f)
X{
X short tag = F_OF_X_TAG;
X short end = F_OF_X_END;
X
X return WRITE(f, tag) &&
X WRITE(f, this->expr) &&
X WRITE(f, end);
X}
X
X/* Delete a member of class f_of_x */
Xstatic struct Region *delete_f_of_x(struct f_of_x *this)
X{
X struct Region *full = full_refresh(this->f.o.g);
X
X destroy_f_of_x(this);
X FreeMem(this, sizeof(struct f_of_x));
X return full;
X}
X
X/* Create a new instance of f_of_x */
Xstruct f_of_x *new_f_of_x(struct graph *g, char *name)
X{
X struct f_of_x *this = AllocMem(sizeof(struct f_of_x), MEMF_CLEAR);
X
X if (this)
X {
X /* Standard init */
X init_function(&this->f, g, name);
X /* Setup methods */
X this->f.save = (void *)save_f_of_x;
X this->f.o.delete = (void *)delete_f_of_x;
X this->f.o.down = (void *)down_f_of_x;
X this->f.o.draw = (void *)draw_f_of_x;
X this->f.o.edit = (void *)edit_f_of_x;
X this->f.o.improve = (void *)improve_f_of_x;
X this->f.o.f2str = (void *)f2str_f_of_x;
X this->f.sizept = sizeof(point);
X return this;
X }
X message(g, "Couldn't create function:", "No memory", (char *)NULL);
X return NULL;
X}
X
X/* Load f_of_x from a file */
Xstruct f_of_x *load_f_of_x(struct graph *g, FILE *f)
X{
X struct f_of_x *this = new_f_of_x(g, "");
X
X if (this)
X {
X short end;
X
X if (READ(f, this->expr) &&
X READ(f, end) &&
X end == F_OF_X_END)
X {
X /* Load standard part */
X load_rest(&this->f, f);
X if (this->f.o.ok = f_of_x_ok(this)) this->f.o.ok = create_f_of_x(th
Xis);
X
X return this;
X }
X delete_f_of_x(this);
X }
X return NULL;
X}
X
X
X
SHAR_EOF
echo "End of archive 1 (of 7)"
# if you want to concatenate archives, remove anything after this line
exit